package com.neusoft.thread4notify;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 等待通知机制，在Java中主要有两种方式。一种是基于wait/notify方法集合synchronized关键字实现的，另一种方法是Condition结合Lock来实现。
 * Condition 将 Object 监视器方法（wait、notify 和 notifyAll）分解成截然不同的对象，以便通过将这些对象与任意 Lock 实现组合使用，
 * 其中，Lock 替代了 synchronized 方法和语句的使用，Condition 替代了 Object 监视器方法的使用。
 * 一个lock对象可以通过多次调用 lock.newCondition() 获取多个Condition对象，也就是说，在一个lock对象上，可以有多个等待队列，
 * 而Object的等待通知在一个Object上，只能有一个等待队列。
 * @author Zhang.Yan
 *
 */
public class ConditionTest 
{

	private final Lock lock=new ReentrantLock();
	
	private final Condition addCondition=lock.newCondition();
	
	private final Condition subCondition=lock.newCondition();
	
	private int num=0;
	
	private List<String> list=new ArrayList<>(10);
	
	public void add()
	{
		lock.lock();
		try
		{
			Thread.sleep(2000);
			//当集合已满,则"添加"线程等待
			while(list.size()==10)
			{
				addCondition.await();
			}
			num++;
			list.add("add apple "+num);
			String tname=Thread.currentThread().getName();
			System.out.println("Thread "+tname+" add apple List Size is "+list.size());
            System.out.println("==============================");
            subCondition.signal();
		} 
		catch (InterruptedException e) 
		{
			e.printStackTrace();
		}
		finally
		{
			lock.unlock();
		}
	}
	
	public void sub()
	{
		lock.lock();
		try
		{
			Thread.sleep(1000);
			while(list.size()==0)
			{
				subCondition.await();
			}
			String str=list.get(0);
			list.remove(0);
			num--;
			String tname=Thread.currentThread().getName();
			System.err.println("Thread "+tname+" sub Banana is ["+str+"]");
	        System.err.println("==============================");
	        addCondition.signal();
		} 
		catch (InterruptedException e) 
		{
			e.printStackTrace();
		}
		finally
		{
			lock.unlock();
		}
	}
	
	
	
	public static void main(String[] args) 
	{
		ConditionTest test=new ConditionTest();
		for(int i=0;i<10;i++)
		{
			 Thread add=new Thread(new AddThread(test));
		     Thread sub=new Thread(new SubThread(test));
		     add.start();
		     sub.start();
		}
	}

}

class AddThread implements Runnable
{

	private ConditionTest conditionTest;
	
	public AddThread(ConditionTest conditionTest)
	{
		this.conditionTest=conditionTest;
	}
	
	@Override
	public void run() 
	{
		conditionTest.add();
	}
	
}

class SubThread implements Runnable
{
	private ConditionTest conditionTest;
	
	public SubThread(ConditionTest conditionTest)
	{
		this.conditionTest=conditionTest;
	}
	
	@Override
	public void run() 
	{
		conditionTest.sub();
	}
	
}
